using Godot;
using Godot.Collections;
using System;
using System.Collections.Generic;

public partial class BaseMapManager : TileMapLayer
{
    PackedScene moveGridPrefb = ResourceLoader.Load<PackedScene>("res://src/prefbas/move_grid/move_grid.tscn");

    #region 子组件
    Node Grids;
    #endregion

    static readonly Vector4 WHITE_COLOR = new Vector4(1, 1, 1, 0.5f);
    static readonly Vector4 GREEN_COLOR = new Vector4(0, 1, 0, 0.5f);
    static readonly Vector4 BLUE_COLOR = new Vector4(0, 0, 1, 0.5f);
    static readonly Vector4 RED_COLOR = new Vector4(1, 0, 0, 0.5f);

    MoveGrid[,] moveGrids;

    void SetGridColor(Vector4 color)
    {
        (moveGrids[Global.MapBorderWidth, Global.MapBorderWidth].Material as ShaderMaterial).SetShaderParameter("mix_color", color);
    }

    public MoveGrid[,] MoveGrids => moveGrids;

    static RobotManager RobotManager => GameManager.Instance.LevelManager.RobotManager;

    protected Vector2I RecoverBuildingPos = Vector2I.Zero;

    public bool IsRobotOnRecoverBuilding(Robot robot)
    {
        return RecoverBuildingPos == LocalToMap(robot.Position);
    }

    public int Distance(Vector2 p1, Vector2 p2)
    {
        var vi1 = LocalToMap(p1);
        var vi2 = LocalToMap(p2);
        return Math.Abs(vi1.X - vi2.X) + Math.Abs(vi1.Y - vi2.Y);
    }

    public int ValidateMapSizeX { get; private set; } = 0;
    public int ValidateMapSizeY { get; private set; } = 0;

    void Init()
    {
        Grids = GetNode<Node>("Grids");
        var r = GetUsedRect();
        var size = r.Size;
        ValidateMapSizeX = size.X + r.Position.X * 2;
        ValidateMapSizeY = size.Y + r.Position.Y * 2;
        moveGrids = new MoveGrid[ValidateMapSizeX, ValidateMapSizeY];

        for (int i = 0; i < ValidateMapSizeX; i++)
        {
            for (int j = 0; j < ValidateMapSizeY; j++)
            {
                var grid = moveGridPrefb.Instantiate<MoveGrid>();
                var v = new Vector2I(i, j);
                grid.MapPos = v;
                grid.Position = MapToLocal(grid.MapPos);
                Grids.AddChild(grid);
                grid.Hide();
                var d = GetCellTileData(v);
                grid.MoveCost = d.GetCustomData("MoveCost").As<Dictionary>();
                moveGrids[i, j] = grid;
            }
        }
    }

    public void ResetMoveGrids()
    {
        foreach (var grid in moveGrids)
        {
            grid?.Reset();
        }
        SetGridColor(WHITE_COLOR);
    }

    public override void _Ready()
    {
        base._Ready();
        Init();
    }

    public List<MoveGrid> MoveGridList { get; private set; }

    public void ShowGrids(Robot robot)
    {
        MoveGridList = GetRobotMoveRange(robot);
        MoveGridList.ForEach(v => v.Show());
        SetGridColor(WHITE_COLOR);
    }

    public List<MoveGrid> GetRobotMoveRange(Robot robot)
    {
        ResetMoveGrids();
        var pos = Vector2I.Zero;
        RobotManager.AllRobotList.ForEach(v =>
        {
            pos = LocalToMap(v.Position);
            moveGrids[pos.X, pos.Y].RobotOnGrid = v;
        });
        pos = LocalToMap(robot.Position);
        var start = moveGrids[pos.X, pos.Y];
        start.Remain = robot.Entity.TotalMovement;
        MoveGridList = FloodFill.Fill(start, moveGrids, robot.Entity.MoveType).FindAll(v => v.RobotOnGrid == null || v.RobotOnGrid.Entity.IsMotherShip);
        if (MoveGridList.Contains(start))
        {
            MoveGridList.Remove(start);
        }
        return MoveGridList;
    }

    public void HideGrids()
    {
        MoveGridList.ForEach(v => v.Hide());
    }

    public bool IsPointInMoveRange(Vector2I v)
    {
        return MoveGridList.Exists(vv => vv.MapPos == v);
    }

    internal void UpdateMoveGridOnRobot(RobotManager rmg)
    {
        foreach (var item in moveGrids)
        {
            if (item != null) item.RobotOnGrid = null;
        }
        rmg.AllRobotList.ForEach(v =>
        {
            if (v.Visible)
            {
                var pos = LocalToMap(v.Position);
                moveGrids[pos.X, pos.Y].RobotOnGrid = v;
            }
        });
        // GD.Print("更新机体于地图上的位置");
    }

    /// <summary>
    /// 获取可攻击范围内的格子
    /// </summary>
    /// <param name="robot"></param>
    /// <param name="rangeMax"></param>
    /// <returns></returns>
    // public List<MoveGrid> GetCanAttackArea(Robot robot, int rangeMin, int rangeMax)`
    public List<MoveGrid> GetSkillCanEffectArea(BaseSkill wp)
    {
        var robot = wp.OwnerRobot;
        var rangeMin = wp.RangeMin;
        var rangeMax = wp.RangeMax;
        var list = new List<MoveGrid>();
        var center = LocalToMap(robot.Position);
        var x = (int)MathF.Abs(center.X);
        var y = (int)MathF.Abs(center.Y);
        for (int i = x - rangeMax; i <= x + rangeMax; i++)
        {
            for (int j = y - rangeMax; j <= y + rangeMax; j++)
            {
                // 在攻击范围内 且 格子上没有机体
                if (Mathf.Abs(i - x) + Mathf.Abs(j - y) >= rangeMin && Mathf.Abs(i - x) + Mathf.Abs(j - y) <= rangeMax && moveGrids.GetLength(0) > i && moveGrids.GetLength(1) > j && moveGrids[i, j] != null)
                {
                    list.Add(moveGrids[i, j]);
                }
            }
        }
        return list;
    }

    /// <summary>
    /// 获取可攻击目标的空格子
    /// </summary>
    /// <param name="robot"></param>
    /// <param name="rangeMax"></param>
    /// <returns></returns>
    public List<MoveGrid> GetCanAttackTargetArea(Robot robot, int rangeMin, int rangeMax)
    {
        var list = new List<MoveGrid>();
        var center = LocalToMap(robot.Position);
        var x = (int)MathF.Abs(center.X);
        var y = (int)MathF.Abs(center.Y);
        for (int i = x - rangeMax; i <= x + rangeMax; i++)
        {
            for (int j = y - rangeMax; j <= y + rangeMax; j++)
            {
                // 在攻击范围内 且 格子上没有机体
                if (Mathf.Abs(i - x) + Mathf.Abs(j - y) >= rangeMin && Mathf.Abs(i - x) + Mathf.Abs(j - y) <= rangeMax && moveGrids.GetLength(0) > i && moveGrids.GetLength(1) > j && moveGrids[i, j] != null && moveGrids[i, j].RobotOnGrid == null)
                {
                    list.Add(moveGrids[i, j]);
                }
            }
        }
        return list;
    }

    internal void ShowSkillEffectRange(BaseSkill weapon)
    {
        MoveGridList = GetSkillCanEffectArea(weapon);
        MoveGridList.ForEach(v => v.Show());
        var color = weapon.AllowTarget == SkillAllowTarget.Enemy ? RED_COLOR : BLUE_COLOR;
        SetGridColor(color);
    }

    /// <summary>
    /// 取移动范围内离目标最近的一些点
    /// </summary>
    /// <param name="nearestTar"></param>
    /// <param name="moveArea"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    internal List<MoveGrid> GetNearestPointInArea(Robot nearestTar, List<MoveGrid> moveArea)
    {
        var list = new List<MoveGrid>();
        var dis = 9999;
        var v = LocalToMap(nearestTar.Position);
        foreach (var item in moveArea)
        {
            var x = Mathf.Abs(v.X - item.MapX);
            var y = Mathf.Abs(v.Y - item.MapY);
            if (x + y < dis)
            {
                list.Clear();
                list.Add(item);
                dis = x + y;
            }
            else if (x + y == dis)
            {
                list.Add(item);
            }
        }
        return list;
    }

    /// <summary>
    /// 获取机体到目标点的移动路径
    /// </summary>
    /// <param name="tarPos"></param>
    /// <returns></returns>
    public List<MoveGrid> GetRobotMovePath(Vector2I tarPos, List<MoveGrid> moveArea = null)
    {
        var list = new List<MoveGrid>();
        MoveGrid tar = null;
        if (moveArea == null)
        {
            tar = moveGrids[tarPos.X, tarPos.Y];
        }
        else
        {
            tar = moveArea.Find(v => v.MapPos == tarPos);
        }
        list.Add(tar);
        while (tar.Previous != null)
        {
            tar = tar.Previous;
            list.Add(tar);
        }
        return list;
    }

    /// <summary>
    /// 找到两个区域的交集
    /// </summary>
    /// <param name="canAttackArea"></param>
    /// <param name="moveArea"></param>
    internal List<MoveGrid> FindSameAreaBetweenAreas(List<MoveGrid> a1, List<MoveGrid> a2)
    {
        List<MoveGrid> list = new();
        foreach (var i in a1)
        {
            foreach (var j in a2)
            {
                if (i.MapPos == j.MapPos)
                {
                    list.Add(i);
                }
            }
        }
        return list;
    }
}
